#ifndef __PINFLOW_THREAD_SAFE_LIST_H__
#define __PINFLOW_THREAD_SAFE_LIST_H__

#include <list>
#include <mutex>
#include <condition_variable>

namespace pinflow {

template <typename T>
class ThreadSafeList {
public:
    using sptr = std::shared_ptr<ThreadSafeList<T>>;

    static sptr Create(int max_size) {
        return std::make_shared<ThreadSafeList<T>>(max_size);
    }

    ThreadSafeList(): max_size_(0) {}
    ~ThreadSafeList() = default;

    ThreadSafeList(const ThreadSafeList&) = delete;
    ThreadSafeList& operator=(const ThreadSafeList&) = delete;
    ThreadSafeList(ThreadSafeList&&) = delete;
    ThreadSafeList& operator=(ThreadSafeList&&) = delete;

    void push_back_one(const T& item) {
        std::lock_guard<std::mutex> lock(mutex_);
        cv_.wait(lock, [this] { return list_.size() < max_size_; });
        list_.push_back(item);
        cv_.notify_one();
    }

    void push_back_batch(const std::list<T>& items) {
        std::lock_guard<std::mutex> lock(mutex_);
        cv_.wait(lock, [&] { return list_.size() + items.size() > max_size_; });
        list_.splice(list_.end(), items);
        cv_.notify_one();
    }

    void pop_front_one(T& item) {
        std::lock_guard<std::mutex> lock(mutex_);
        cv_.wait(lock, [this] { return !list_.empty(); });
        item = list_.front();
        list_.pop_front();
        cv_.notify_one();
    }

    void pop_front_batch(std::list<T>& items) {
        std::unique_lock<std::mutex> lock(mutex_);
        cv_.wait(lock, [this] { return !list_.empty(); });
        items.splice(items.end(), list_);
        cv_.notify_one();
    }

private:
    std::list<T> list_;
    std::mutex mutex_;
    std::condition_variable cv_;
    int max_size_;
};

}  // namespace pinflow


#endif  // __PINFLOW_THREAD_SAFE_LIST_H__